package com.gzz.demo.security;
import java.security.MessageDigest;

/**
 *MD5算法具有以下特点：
 *
 * 1、压缩性：任意长度的数据，算出的MD5值长度都是固定的。
 *
 * 2、容易计算：从原数据计算出MD5值很容易。
 *
 * 3、抗修改性：对原数据进行任何改动，哪怕只修改1个字节，所得到的MD5值都有很大区别。
 *
 * 4、强抗碰撞：已知原数据和其MD5值，想找到一个具有相同MD5值的数据（即伪造数据）是非常困难的。
 *
 * MD5属于单向散列加密
 *
 *      单向散列加密是通过对不同输入长度的信息进行散列计算，得到固定长度的输出，单向的（不可逆）。
 *
 *      用户的密码通过此加密将密文存在数据库当中，登录时根据用户输入的密码进行加密后与数据库中的密文进行对比。
 *
 *      这样即使平台数据库被“拖库”，也不会造成用户的密码泄漏。
 *
 *      代表：MD5算法     SHA算法
 *
 * MD5功能：
 *     输入任意长度的信息，经过处理，输出为128位的信息（数字指纹）；
 *     不同的输入得到的不同的结果（唯一性）；
 *     根据128位的输出结果不可能反推出输入的信息（不可逆）；
 * MD5属不属于加密算法：
 *     认为不属于的人是因为他们觉得不能从密文（散列值）反过来得到原文，即没有解密算法，所以这部分人认为MD5只能属于算法，不能称为加密算法；
 *     认为属于的人是因为他们觉得经过MD5处理后看不到原文，即已经将原文加密，所以认为MD5属于加密算法；我个人支持后者。
 * MD5用途：
 *     1、防止被篡改：
 *      比如发送一个电子文档，发送前，我先得到MD5的输出结果a。然后在对方收到电子文档后，对方也得到一个MD5的输出结果b。如果a与b一样就代表中途未被篡改。
 *          2）比如我提供文件下载，为了防止不法分子在安装程序中添加木马，我可以在网站上公布由安装文件得到的MD5输出结果。
 *          3）SVN在检测文件是否在CheckOut后被修改过，也是用到了MD5.
 *     2、防止直接看到明文：
 *          现在很多网站在数据库存储用户的密码的时候都是存储用户密码的MD5值。这样就算不法分子得到数据库的用户密码的MD5值，也无法知道用户的密码(其实这样是不安全的，后面我会提到)。
 *          （
 *          比如在UNIX系统中用户的密码就是以MD5（或其它类似的算法）经加密后存储在文件系统中。当用户登录的时候，
 *          系统把用户输入的密码计算成MD5值，然后再去和保存在文件系统中的MD5值进行比较，进而确定输入的密码是否正确。通过这样的步骤，系统在并不知道用户密码的明码的情况下就可以确定用户登录系统的合法性。
 *          这不但可以避免用户的密码被具有系统管理员权限的用户知道，而且还在一定程度上增加了密码被破解的难度。
 *          ）
 *     3、防止抵赖（数字签名）：
 *     这需要一个第三方认证机构。
 *          例如A写了一个文件，认证机构对此文件用MD5算法产生摘要信息并做好记录。若以后A说这文件不是他写的，权威机构只需对此文件重新产生摘要信息，然后跟记录在册的摘要信息进行比对，
 *          相同的话，就证明是A写的了。这就是所谓的“数字签名”。
 *
 */
public class TestSecurity {
    /**
     * 加签
     *
     * @param plain   原串
     * @param charset 字符集编码
     * @return
     */
    public static String sign(String plain, String charset) {
        StringBuffer md5Str = new StringBuffer(32);
        try {
            MessageDigest md = MessageDigest.getInstance("MD5");
            if (charset == null || "".equals(charset)) {
                charset = "UTF-8";
            }
            byte[] array = md.digest(plain.getBytes(charset));
            for (int i = 0; i < array.length; i++) {
                md5Str.append(Integer.toHexString((array[i] & 0xFF) | 0x100).toUpperCase().substring(1, 3));
            }
        } catch (Exception ex) {
            ex.printStackTrace();
        }
        return md5Str.toString();
    }

    /**
     * 验签
     *
     * @param plain   原串
     * @param signed  签名串
     * @param charset 字符集编码
     * @return
     */
    public static boolean verifySign(String plain, String signed, String charset) {
        String plainSigned = sign(plain, charset);
        return plainSigned.equals(signed);
    }

    public static void main(String[] args){
        String password = "123456"; //我的密码，需要加密的对象

        //使用MD5加签，
        String signMessage = TestSecurity.sign(password,"UTF-8");
        System.out.println("signMessage:"+signMessage);

        //验签1
        System.out.println("是否一致："+signMessage.equals(TestSecurity.sign(password,"UTF-8")));

        //验签2
        String passwordOther = "654321";
        System.out.println("是否一致："+signMessage.equals(TestSecurity.sign(passwordOther,"UTF-8")));
    }
}
